package com.thinkbiganalytics.db; /*- * #%L * thinkbig-feed-manager-controller * %% * Copyright (C) 2017 ThinkBig Analytics * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.sql.DataSource; /** * A Connection Pooling service to return a new DataSource. * <p>Used for the {@code DBSchemaParser} class.</p> */ public class PoolingDataSourceService { /** * Cache of data sources. * * <p>Expires unused entries to avoid long-term caching of data sources with invalid credentials.</p> */ private static final LoadingCache<DataSourceProperties, DataSource> DATA_SOURCES = CacheBuilder.newBuilder() .expireAfterAccess(60, TimeUnit.MINUTES) .build(new CacheLoader<DataSourceProperties, DataSource>() { @Override public DataSource load(@Nonnull final DataSourceProperties key) throws Exception { return createDatasource(key); } }); /** * Gets the data source using the specified properties. * * @param props the data source properties * @return the data source */ public static DataSource getDataSource(@Nonnull final DataSourceProperties props) { try { return DATA_SOURCES.get(props); } catch (final ExecutionException e) { if (e.getCause() != null) { throw Throwables.propagate(e.getCause()); } else { throw new RuntimeException(e); } } } private static DataSource createDatasource(DataSourceProperties props) { DataSourceBuilder builder = DataSourceBuilder.create().url(props.getUrl()).username(props.getUser()).password(props.getPassword()); if (StringUtils.isNotBlank(props.getDriverClassName())) { builder.driverClassName(props.getDriverClassName()); } DataSource ds = builder.build(); if (props.isTestOnBorrow() && StringUtils.isNotBlank(props.getValidationQuery())) { if (ds instanceof org.apache.tomcat.jdbc.pool.DataSource) { ((org.apache.tomcat.jdbc.pool.DataSource) ds).setTestOnBorrow(true); ((org.apache.tomcat.jdbc.pool.DataSource) ds).setValidationQuery(props.getValidationQuery()); } else if (ds instanceof org.apache.commons.dbcp2.BasicDataSource) { ((org.apache.commons.dbcp2.BasicDataSource) ds).setValidationQuery(props.getValidationQuery()); ((org.apache.commons.dbcp2.BasicDataSource) ds).setTestOnBorrow(true); } else if (ds instanceof org.apache.commons.dbcp.BasicDataSource) { ((org.apache.commons.dbcp.BasicDataSource) ds).setValidationQuery(props.getValidationQuery()); ((org.apache.commons.dbcp.BasicDataSource) ds).setTestOnBorrow(true); } } return ds; } public static class DataSourceProperties { String user; String password; String url; String driverClassName; boolean testOnBorrow; String validationQuery; public DataSourceProperties(String user, String password, String url) { this.user = user; this.password = password; this.url = url; } public DataSourceProperties(String user, String password, String url, String driverClassName, boolean testOnBorrow, String validationQuery) { this.user = user; this.password = password; this.url = url; this.driverClassName = driverClassName; this.testOnBorrow = testOnBorrow; this.validationQuery = validationQuery; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public boolean isTestOnBorrow() { return testOnBorrow; } public void setTestOnBorrow(boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DataSourceProperties that = (DataSourceProperties) o; return Objects.equals(user, that.user) && Objects.equals(password, that.password) && Objects.equals(url, that.url) && Objects.equals(driverClassName, that.driverClassName); } @Override public int hashCode() { return Objects.hash(user, password, url, driverClassName); } } /** * Instances of {@code PoolingDataSourceService} may not be constructed. * * @throws UnsupportedOperationException always */ private PoolingDataSourceService() { throw new UnsupportedOperationException(); } }